#include <jni.h>
#include <string>
#include <malloc.h>
#include "MyLog.h"
#import <memory.h>
#import "MyDS.h"
#include "uitls/StringUtils.h"

//静态变量, 用于判断是否已经初始化
static bool hasInit = false;

static const char *CLZ_NAME = "com/caldremch/storage/a";
//const char *app_signature = "C9C59636571FC8C0BEDE25C957AA84875C233961";
const char *app_signature = "A72230746A77673069DC4DD77E04A003C54334B5";


static jboolean verifyKey(JNIEnv *env, char *hexChar);

static void verifyInit(){
    if (!hasInit){
//        jhare
    }
}
static jstring getFromNaReg(JNIEnv *env, jclass clz, jobject obj) {

    const char *str = "来自动态注册";
    jstring phone = env->NewStringUTF("phone");
    return phone;
}

static jstring getDeviceIdFromJNI(JNIEnv *env , jclass, jobject obj){


    const char *str = "来自动态注册 getDeviceId";
    return env->NewStringUTF(str);

}



static jboolean verifyKey(JNIEnv *env, char *hexChar) {

    return (jboolean) (strcmp(hexChar, app_signature) == 0);

};


//类签名后面要添加";"分号, 不然会报错
static void init(JNIEnv *env, jclass, jobject contextObj) {
    log_error("初始化--->签名校验 %s", "");


    try {

        jclass contextClz = env->GetObjectClass(contextObj);
//    //context.getPackageManager()
        jmethodID methodId = env->GetMethodID(contextClz, "getPackageManager",
                                              "()Landroid/content/pm/PackageManager;");
        jobject pmObj = env->CallObjectMethod(contextObj, methodId);

        if (pmObj == NULL) {
            log_error("error: %s", "PackageManager获取失败");
            return;
        }

         //context.getPackageName();
        methodId = env->GetMethodID(contextClz, "getPackageName", "()Ljava/lang/String;");
        //what's static_cast<jstring>
        jstring packageName = (jstring) (env->CallObjectMethod(contextObj, methodId));
        if (packageName == NULL) {
            log_error("error: %s", "包名获取失败");
            return;
        }
        log_info("包名: %s", StringUtils::jstring2char(env, packageName));

//
        jclass pmClz = env->GetObjectClass(pmObj);
        methodId = env->GetMethodID(pmClz, "getPackageInfo",
                                    "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;");
        //pmClz已无用, 释放资源
        env->DeleteLocalRef(pmClz);

        //获取PackageInfo对象实例
        jobject piObj = env->CallObjectMethod(pmObj, methodId, packageName, 64);
        jclass piClz = env->GetObjectClass(piObj);
        // Signature[] signatures = packageInfo.signatures;
        jfieldID signFid = env->GetFieldID(piClz, "signatures", "[Landroid/content/pm/Signature;");
        jobjectArray signObjArray = (jobjectArray) env->GetObjectField(piObj, signFid);
        env->DeleteLocalRef(piClz);
        if (signObjArray == NULL) {
            log_error("error: %s", "PackageInfo.signatures[] is null");
            return;
        }

        //获取签名数组第一位
        jobject signObj = env->GetObjectArrayElement(signObjArray, 0);
        env->DeleteLocalRef(piObj);
        //Signature.toByteArray()
        jclass signClz = env->GetObjectClass(signObj);
        methodId = env->GetMethodID(signClz, "toByteArray", "()[B");
        jbyteArray signByteArray = (jbyteArray) env->CallObjectMethod(signObj, methodId);

        /***********************sha1校验代码****************************/

        //new ByteArrayInputStream
        jclass baisClz = env->FindClass("java/io/ByteArrayInputStream");
        methodId = env->GetMethodID(baisClz, "<init>", "([B)V");
        jobject baisObj = env->NewObject(baisClz, methodId, signByteArray);


        ////CertificateFactory.getInstance("X.509")
        jclass cfClz = env->FindClass("java/security/cert/CertificateFactory");
        methodId = env->GetStaticMethodID(cfClz, "getInstance",
                                          "(Ljava/lang/String;)Ljava/security/cert/CertificateFactory;");
        jstring x509Str = env->NewStringUTF("X.509");
        jobject cfObj = env->CallStaticObjectMethod(cfClz, methodId, x509Str);

        // certificateFactory.generateCertificate
        methodId = env->GetMethodID(cfClz, "generateCertificate",
                                    "(Ljava/io/InputStream;)Ljava/security/cert/Certificate;");
        jobject x509Obj = env->CallObjectMethod(cfObj, methodId, baisObj);

        env->DeleteLocalRef(cfClz);

        jclass mdClz = env->FindClass("java/security/MessageDigest");
        methodId = env->GetStaticMethodID(mdClz, "getInstance",
                                          "(Ljava/lang/String;)Ljava/security/MessageDigest;");
        jstring sha1Str = env->NewStringUTF("SHA1");
        jobject mdObj = env->CallStaticObjectMethod(mdClz, methodId, sha1Str);

        jclass x509Clz = env->GetObjectClass(x509Obj);
        methodId = env->GetMethodID(x509Clz, "getEncoded", "()[B");
        jbyteArray certByteArray = (jbyteArray) env->CallObjectMethod(x509Obj, methodId);
        env->DeleteLocalRef(x509Clz);


        methodId = env->GetMethodID(mdClz, "digest", "([B)[B");
        jbyteArray sha1ByteArray = (jbyteArray) env->CallObjectMethod(mdObj, methodId,
                                                                      certByteArray);
        env->DeleteLocalRef(mdClz);
        //进行校验
        StringUtils::toHexString(env, sha1ByteArray);
        if(verifyKey(env, StringUtils::toHexString(env, sha1ByteArray))){
            log_info("验证通过%s","");
        } else{
            log_info("验证失败%s","");
        }
        /***********************sha1校验代码****************************/

    } catch (jthrowable e) {
        log_error("error: %s", "异常了4");
    }


}


static JNINativeMethod methods[] = {
        {
                "getTestStr", "()Ljava/lang/String;",  (void *) getFromNaReg
        },
        {
                "init",       "(Ljava/lang/Object;)V", (void *) init
        },
        {
                "getDeviceId",       "()Ljava/lang/String;", (void *) getDeviceIdFromJNI
        },


};

//根据函数映射表注册函数
static int regNativeMethods(JNIEnv *env,
                            const char *className,
                            const JNINativeMethod *gMethods,
                                 size_t num
) {


    //获取包名类
    jclass clz;
    clz = env->FindClass(className);
    if (clz == NULL) {
        log_error("Class not found","");
        return JNI_ERR;
    }

    //注册以下函数
    for (int i = 0; i < num; ++i) {
        log_debug("RegisterNatives method -> %s(%s) | fun-> %lld", gMethods[i].name,
                  gMethods[i].signature, (INTPTR) gMethods[i].fnPtr);
    }

    //动态注册函数
    if (env->RegisterNatives(clz, gMethods, num) < 0) {
        log_error("RegisterNatives failed","");
        return JNI_ERR;
    }

    //删除本地引用
    env->DeleteLocalRef(clz);

    return JNI_OK;
}


/**
 * JNI_OnLoad()函数
   JNI_OnLoad()函数有两个重要的作用：

1 指定 jni 版本：告诉 JVM 该组件使用哪一个 jni 版本(若未提供JNI_OnLoad()函数，JVM 会默认该使用最老的 JNI 1.1版本)，如果要使用新版本的JNI，例如JNI 1.4版，则必须由 JNI_OnLoad() 函数返回常量 JNI_VERSION_1_4 (该常量定义在 jni.h 中) 来告知 JVM 。
2 一系列初始化操作，当 JVM 执行到 System.loadLibrary() 函数时，会立即调用 JNI_OnLoad() 方法，因此在该方法中进行各种资源的初始化操作最为恰当，
 */
//当 java 通过 System.loadLibrary 加载完 JNI 动态库后，紧接着会调用 JNI_OnLoad 的函数。
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {


    //获取JNIEnv
    JNIEnv *env;
    if (jvm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        return JNI_ERR;
    }

    if (regNativeMethods(env, CLZ_NAME,  methods,sizeof(methods)/sizeof(methods[0]))< 0){
        return JNI_ERR;
    }

    return JNI_VERSION_1_4;

}
